The Code Project
View our advertisers Advertise with us
All Topics, C#, .NET >> Multimedia >> Audio

A full-duplex audio player in C# using the waveIn/waveOut APIs
By Ianier Munoz

An article on low-level audio capture and playback using the waveIn/waveOut APIs through P/Invoke in C#. 
  C#
Windows (WinXP, Win2K, Win2003, Win95, Win98, WinME), .NET (.NET 1.1, .NET 1.0)
Win32, VS (VS.NET2002)
Dev
  Posted 1 Sep 2003
Articles by this author
91,249 views
Search:
Ultimate Combo
MFC tools $399
Send to a friend
My Profile
My Settings
My Articles
My Bookmarks
Sign out
 


Print Broken Article? Bookmark Discuss
32 votes for this article.
Popularity: 6.36. Rating: 4.23 out of 5.
  • You are signed up for one or more newsletters but unfortunately we are unable to send you emails. Please click here to have an email sent that will allow us to confirm your email address.

Sample Image - cswavrec.gif

Introduction

As I already mentioned in my article A low-level audio player in C#, there are no built-in classes in the .NET framework for dealing with sound. This holds true not only for audio playback, but also for audio capture.

It should be noted, though, that the Managed DirectX 9 SDK does include classes for high-level and low-level audio manipulation. However, sometimes you don’t want your application to depend on the full DX 9 runtime, just to do basic sound playback and capture, and there are also some areas where Managed DirectSound doesn’t help at all (for example, multi-channel sound playback and capture).

Nevertheless, I strongly recommend you to use Managed DirectSound for sound playback and capture unless you have a good reason for not doing so.

This article describes a sample application that uses the waveIn and waveOut APIs in C# through P/Invoke to capture an audio signal from the sound card’s input, and play it back (almost) at the same time.

Using the code

The sample code reuses the WaveOutPlayer class from my article A low-level audio player in C#. The new classes in this sample are WaveInRecorder and FifoStream.

The FifoStream class extends System.IO.Stream to implement a FIFO (first-in first-out) of bytes. The overridden Write method adds data to the FIFO’s tail, and the Read method peeks and removes data from the FIFO’s head. The Length property returns the amount of buffered data at any time. Calling Flush will clear all pending data.

The WaveInRecorder class is analogous to the WaveOutPlayer class. In fact, if you look at the source files, you’ll notice that the implementations of these classes are very similar. As with WaveOutPlayer, the interface of this class has been reduced to the strict minimum.

Creating an instance of WaveInRecorder will cause the system to start recording immediately. Here’s the code that creates the WaveOutPlayer and WaveInRecorder instances.

private void Start()
{
    Stop();
    try
    {
        WaveLib.WaveFormat fmt = new WaveLib.WaveFormat(44100, 16, 2);
        m_Player = new WaveLib.WaveOutPlayer(-1, fmt, 16384, 3, 
                        new WaveLib.BufferFillEventHandler(Filler));
        m_Recorder = new WaveLib.WaveInRecorder(-1, fmt, 16384, 3, 
                        new WaveLib.BufferDoneEventHandler(DataArrived));
    }
    catch
    {
        Stop();
        throw;
    }
}

The WaveInRecorder constructor takes five parameters. Except for the last parameter, their meaning is the same as in WaveOutPlayer.

The first parameter is the ID of the wave input device that you want to use. The value -1 represents the default system device, but if your system has more than one sound card, then you can pass any number from 0 to the number of installed sound cards minus one, to select a particular device.

The second parameter is the format of the audio samples.

The third and forth parameters are the size of the internal wave buffers and the number of buffers to allocate. You should set these to reasonable values. Smaller buffers will give you less latency, but the captured audio may have gaps on it if your computer is not fast enough.

The fifth and last parameter is a delegate that will be called periodically as internal audio buffers are full of captured data. In the sample application we just write the captured data to the FIFO, like this:

private void DataArrived(IntPtr data, int size)
{
    if (m_RecBuffer == null || m_RecBuffer.Length < size)
        m_RecBuffer = new byte[size];
    System.Runtime.InteropServices.Marshal.Copy(data, m_RecBuffer, 0, size);
    m_Fifo.Write(m_RecBuffer, 0, m_RecBuffer.Length);
}

Similarly, the Filler method is called every time the player needs more data. Our implementation just reads the data from the FIFO, as shown below:

private void Filler(IntPtr data, int size)
{
    if (m_PlayBuffer == null || m_PlayBuffer.Length < size)
        m_PlayBuffer = new byte[size];
    if (m_Fifo.Length >= size)
        m_Fifo.Read(m_PlayBuffer, 0, size);
    else
        for (int i = 0; i < m_PlayBuffer.Length; i++)
            m_PlayBuffer[i] = 0;
    System.Runtime.InteropServices.Marshal.Copy(m_PlayBuffer, 
                                                 0, data, size);
}

Note that we declared the temporary buffers m_RecBuffer and m_PlayBuffer as member fields in order to improve performance by saving some garbage collections.

To stop streaming, just call Dispose on the player and capture objects. We also need to flush the FIFO so that the next time Start is called there is no residual data to play.

private void Stop()
{
    if (m_Player != null)
        try
        {
            m_Player.Dispose();
        }
        finally
        {
            m_Player = null;
        }
    if (m_Recorder != null)
        try
        {
            m_Recorder.Dispose();
        }
        finally
        {
            m_Recorder = null;
        }
    m_Fifo.Flush(); // clear all pending data
}

Conclusion

This sample demonstrates how to combine the waveIn and waveOut APIs in C#. As an exercise, you may want to combine this code with the audio effect framework in the article Programming Audio Effects in C#, to apply effects to a live audio input in real-time, although latency may be an issue for certain applications.

About Ianier Munoz


Ianier Munoz lives in France and works as a senior consultant and analyst for an international consulting firm. His specialty is in multimedia applications, and he has authored some popular software, such as American DJ's Pro-Mix, Chronotron and Adapt-X.

Click here to view Ianier Munoz's online profile.


Other popular articles:

[Top] Rate this Article for us!     PoorExcellent  

FAQ  Noise tolerance    Search comments  
  View    Per page  
New Message Msgs 86 to 110 of 149 (Total: 149) (Refresh) First Prev Next     
Subject  Author  Date 
  Selecting Line-In or Microphone Unconfirmed/Anonymous posting  jasonrowland  18:16 27 Oct '04 
  Re: Selecting Line-In or Microphone   Ianier Munoz  1:38 28 Oct '04 
  display 2 i.e. to wave out device is present   MohdYounisKhan  1:59 27 Oct '04 
  Re: display 2 i.e. to wave out device is present   Ianier Munoz  1:41 28 Oct '04 
  Performance   kgoodrich  9:51 15 Oct '04 
  Re: Performance   Ianier Munoz  3:13 17 Oct '04 
  Device Names   kawaic  15:20 30 Sep '04 
  Re: Device Names   Ianier Munoz  6:47 6 Oct '04 
  Some questions   Hackera  20:19 24 Sep '04 
  can i listen other people with my modem?   Hackera  11:15 24 Sep '04 
  Recorder   kgoodrich  11:04 9 Sep '04 
  Re: Recorder   kgoodrich  15:51 20 Sep '04 
  How can i save the wave into a file?   jeasonzhao  23:43 7 Sep '04 
  Re: How can i save the wave into a file?   Ianier Munoz  1:22 8 Sep '04 
 
To create WAV files you can use the WaveWriter class written by Mr. Cardoso in this article: http://www.codeproject.com/csharp/CSharpRipper.asp. All you have to do is replacing the code inside DataArrived so that it writes data to Cardoso's wave stream. That is, in MainForm.cs replace the line
m_Fifo.Write(m_RecBuffer, 0, m_RecBuffer.Length);
by
m_Stream.Write(m_RecBuffer, 0, m_RecBuffer.Length);
where m_Stream is a wav stream allocated as described in Cardoso's article.
You should also remove everything concerning m_Player, since you only want to implement recording.
Hope it helps,

Ianier Munoz
http://www.chronotron.com/

 
[Reply][Email][View Thread][Get Link] Score: 3.0 (1 vote).
  Re: How can i save the wave into a file?   Marc Meuleman  10:00 11 Mar '05 
  Re: How can i save the wave into a file?   Ianier Munoz  1:18 21 Mar '05 
  Re: How can i save the wave into a file? Unconfirmed/Anonymous posting  Anonymous  4:51 5 Apr '05 
  Re: How can i save the wave into a file?   maxud  4:27 13 Apr '05 
  Re: How can i save the wave into a file?   SujithNarayanannair  0:45 29 Apr '05 
  Re: How can i save the wave into a file?   cbranje  12:33 31 Aug '05 
  Re: How can i save the wave into a file?   SpencerDog  13:32 1 Nov '05 
  How Can i record the sound from the MODEM?   wangxian11  2:30 1 Sep '04 
  Re: How Can i record the sound from the MODEM?   Ianier Munoz  6:31 3 Sep '04 
  Re: How Can i record the sound from the MODEM?   wangxian11  3:47 4 Sep '04 
  Re: How Can i record the sound from the MODEM?   Ianier Munoz  3:22 6 Sep '04 
Last Visit: 4:17 Monday 6th March, 2006 First Prev Next     

General comment    News / Info    Question    Answer    Joke / Game    Admin message


All Topics, C#, .NET >> Multimedia >> Audio
Updated: 1 Sep 2003
Article content copyright Ianier Munoz, 2003
everything else Copyright © CodeProject, 1999-2006.
Web11 | Advertise on The Code Project | Privacy

The Ultimate Toolbox • ASP Alliance • Developer Fusion • Developersdex • DevGuru • Programmers Heaven • Planet Source Code • Tek-Tips Forums •